public class Person implements Runnable
{
    private static int numHallA, numHallB;
    private static boolean someoneCrossing = false;

    public static final long WAIT_TIME = 1000L;
    public static final long WAIT_OFFSET = 1000L;

    private static final long CROSSING_TIME = 3000L;
    private static final long CROSSING_OFFSET = 2000L;
    private static final float CROSSING_PROBABILITY = .8f;

    public static Object corridor = new Object();

    private Room room;

    public Person(int numHallA, int numHallB, Room room)
    {
        this.numHallA = numHallA;
        this.numHallB = numHallB;
        this.room = room;
    }

    private void handleSleep(long millis)
    {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException exc) {
            exc.printStackTrace();
            System.exit(1);
        }
    }

    private void handleWait(Object lock)
    {
        try {
            lock.wait();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    private Room oppositeRoom()
    {
        return room == Room.A ? Room.B : Room.A;
    }

    private void cross()
    {
        synchronized (corridor)
        {
            while (someoneCrossing)
                handleWait(corridor);
            someoneCrossing = true;
            handleSleep((long) (Math.random() * CROSSING_TIME) + CROSSING_OFFSET);
            numHallA += room == Room.A ? -1 : 1;
            numHallB += room == Room.B ? -1 : 1;
            room = oppositeRoom();
            someoneCrossing = false;
            corridor.notifyAll();
        }
    }

    public void run()
    {
        while (true)
        {
            handleSleep((long) (Math.random() * WAIT_TIME) + WAIT_OFFSET);
            double decision = Math.random();
            if (decision >= CROSSING_PROBABILITY)
            {
                System.out.println(Thread.currentThread().getId() + " is going to cross to room " + oppositeRoom());
                cross();
                System.out.println(Thread.currentThread().getId() + " crossed to room " + room);
                System.out.printf("%d in hall A and %d in hall B\n", numHallA, numHallB);
            }
        }
    }
}
